﻿/*********************************************************************
 * \file   archives.h
 * \brief  archives （档案馆）：用于向各模块提供基于 key-value 的数据存取功能
 *			1、由于后端存贮可能各种不同的形式：文件系统、数据库、Redis 等等，
 *		所以 archives 主要是做一个抽象和隔离，提供一个一致的，基于 key-value
 *		的持久化数据存、取方式；
 *			2、数据存、取时，考虑到大数据量和效率的问题，各模块对 archives
 *		的访问应当是本地化的进程内访问，而非通过 HTTP、websocket、socket 等；
 *
 * \author boyun
 * \date   June 2023
 *********************************************************************/

#pragma once

#include <fstream>
#include <map>
#include <string>
#include <vector>

#include "http_rep.h"
#include "serializer.hpp"

namespace ar {
/**
 * 可供存、取的数据类型：
 *  - 字符串类型
 *  - 二进制数据列表（其实这个和字符串类型有点重复，但用于表明不同的意义）
 */
using archives_data = std::vector<uint8_t>;

/**
 * 数据存、取接口定义
 */
template<typename T>
class archives {
public:
    /**
     * 数据记录：
     *  这里特地将“数据记录”抽象出来，一个数据记录，对应“档案馆”里的一份数据集
     */
    class record {
    public:
        explicit record()
            : m_modified(false)
            , m_id()
            , m_version("1.0.0.0") {}
        explicit record(const T& id, const std::string& version = "1.0.0.0")
            : m_modified(false)
            , m_id(id)
            , m_version(version) {
            assert(!m_version.empty());
        }
        virtual ~record() {}

        T&           id() { return m_id; }
        void         id(const T& id) { m_id = id; }
        std::string& version() { return m_version; }
        void         version(const std::string& v) {
            assert(!v.empty());
            m_version = v;
        }

        /**
         * 加载数据：默认实现是从本地文件加载
         *
         * \param filename 指定加载的数据记录名（默认是本地文件名）
         * \return 如加载成功，返回数据记录的ID（UUID）
         */
        virtual response_t<T> load(const std::string& filename) {
            if (!std::filesystem::exists(filename) || !std::filesystem::is_regular_file(filename))
                return -1;

            // 打开文件
            std::ifstream input_file(filename, std::ios::in | std::ifstream::binary);

            // 读取内容到二进制数组
            std::vector<char> data((std::istreambuf_iterator<char>(input_file)),
                                   std::istreambuf_iterator<char>());

            msgpack::serializer sr(data.data(), data.size());

            // 先读取自身的一些属性
            sr >> m_id >> m_version >> m_props;

            // 读取数据
            sr >> m_map_datas;

            m_modified = false;

            return response_t<T>(0, "success", m_id);
        }

        /**
         * 保存数据：默认实现保存到本地文件
         *
         * \param filename 保存目的地（文件名）
         * \return
         */
        virtual response_t<int> save(const std::string& filename) {
            if (std::filesystem::exists(filename) && !std::filesystem::is_regular_file(filename))
                return -1;

            msgpack::serializer sr;
            sr << m_id << m_version << m_props << m_map_datas;
            m_modified = false;

            // 打开文件
            std::ofstream output_file(filename, std::ios::out | std::ofstream::binary);

            // 写入数据
            std::copy(
                sr.data(), sr.data() + sr.size(), std::ostreambuf_iterator<char>(output_file));

            return 0;
        }

        // 查询数据是否存在
        virtual bool exist(const std::string& data_id) { return m_map_datas.contains(data_id); }

        // 取数据
        virtual response_t<archives_data> get(const std::string& data_id) {
            if (!exist(data_id))
                return -1;

            return response_t<archives_data>(0, "success", m_map_datas.at(data_id));
        }

        // 存数据
        virtual response_t<int> put(const std::string& data_id, archives_data& data) {
            m_map_datas[data_id] = data;
            m_modified           = true;
            return 0;
        }

        // 存数据（以字符串形式）
        virtual response_t<int> put(const std::string& data_id, const std::string& data) {
            m_map_datas[data_id].clear();
            m_map_datas[data_id].assign(data.begin(), data.end());
            m_modified = true;
            return 0;
        }

        // 清除数据
        virtual response_t<int> erase(const std::string& data_id) {
            m_map_datas.erase(data_id);
            m_modified = true;
            return 0;
        }

        // 缓存的数据，是否被修改过（save 之后，重新回到未修改状态）
        virtual bool modified() { return m_modified; }

    private:
        // 本地缓存的多个数据项
        std::map<std::string, archives_data> m_map_datas;

        bool m_modified;   // 数据脏标志，有未保存的修改时为 true

        T                                  m_id;        // 唯一标识
        std::string                        m_version;   // 版本号，默认是项目的版本
        std::map<std::string, std::string> m_props;     // 数据记录本身的一些属性
    };


public:
    explicit archives() {}
    virtual ~archives() {}

    /**
     * 新建一个数据记录
     *
     * \return 返回新建数据项的索引ID
     */
    virtual response_t<T> new_record(std::shared_ptr<record> r) {
        if (nullptr == r)
            return -1;

        if (exist(r->id()))
            return -2;

        assert(!r->version().empty());

        m_map_records[r->id()] = r;

        return response_t<T>(0, "success", r->id());
    }

    /**
     * 加载数据：默认实现是从本地文件加载
     *
     * \param filename 指定加载的数据记录名（默认是本地文件名）
     * \return 如加载成功，返回数据记录的ID（UUID）
     */
    virtual response_t<T> load_record(const std::string& filename) {
        auto new_record = std::make_shared<record>();
        auto r          = new_record->load(filename);

        if (!r)
            m_map_records[r.data()] = new_record;

        return r;
    }

    /**
     * 保存数据：默认实现保存到本地文件
     *
     * \param record_id 需要保存的数据记录
     * \param filename 保存目的地（文件名）
     * \return
     */
    virtual response_t<int> save_record(const T& record_id, const std::string& filename) {
        if (!exist(record_id))
            return -1;

        return m_map_records.at(record_id)->save(filename);
    }

    /**
     * 从本地缓存中，卸载数据记录（不负责保存）
     *
     * \param record_id 需卸载的数据记录
     * \return
     */
    virtual response_t<int> unload_record(const T& record_id, bool fail_when_modified) {
        if (!exist(record_id))
            return -1;

        if (fail_when_modified && modified(record_id))
            return -2;

        m_map_records.erase(record_id);
        return 0;
    }

    /**
     * 查询数据记录是否存在
     *
     * \param record_id 数据记录ID
     * \return
     */
    virtual bool exist(const T& record_id) { return m_map_records.contains(record_id); }

    /**
     * 查询数据记录中，某数据项是否存在
     *
     * \param record_id 数据记录ID
     * \param data_id 数据项ID
     * \return
     */
    virtual bool exist(const T& record_id, const std::string& data_id) {
        if (!exist(record_id))
            return false;

        return m_map_records.at(record_id)->exist(data_id);
    }

    // 是否有未保存的修改
    virtual bool modified(const T& record_id) {
        if (!exist(record_id))
            return false;
        return m_map_records.at(record_id)->modified();
    }

    // 取数据
    virtual response_t<archives_data> get(const T& record_id, const std::string& data_id) {
        if (!exist(record_id))
            return -1;

        return m_map_records.at(record_id)->get(data_id);
    }

    // 存数据
    virtual response_t<int> put(const T& record_id, const std::string& data_id,
                                archives_data& data) {
        if (!exist(record_id))
            return -1;

        return m_map_records.at(record_id)->put(data_id, data);
    }

    virtual response_t<int> put(const T& record_id, const std::string& data_id,
                                const std::string& data) {
        if (!exist(record_id))
            return -1;

        return m_map_records.at(record_id)->put(data_id, data);
    }

    virtual response_t<int> erase(const T& record_id, const std::string& data_id) {
        if (!exist(record_id))
            return -1;

        return m_map_records.at(record_id)->erase(data_id);
    }

private:
    // 本地缓存的多个数据记录
    std::map<T, std::shared_ptr<record>> m_map_records;
};
}   // namespace ar